//#define WIRE_IN_DIRECT3D

using System;
using System.Drawing;

#if WIRE_IN_DIRECT3D
using Microsoft.DirectX;
using Microsoft.DirectX.Direct3D;
#endif

namespace Meta
{
	public abstract class VectorSpace {
		static VectorSpace() {
			Register(typeof(Color), new ColorVectorSpace());
			Register(typeof(Point), new PointVectorSpace());
			Register(typeof(PointF), new PointFVectorSpace());
#if WIRE_IN_DIRECT3D
			Register(typeof(Vector3), new Vector3VectorSpace());
			Meta.Writer.SetDisclosure(typeof(Vector3), new string[] {"X", "Y", "Z"});
			Register(typeof(Vector4), new Vector4VectorSpace());
			Meta.Writer.SetDisclosure(typeof(Vector4), new string[] {"X", "Y", "Z", "W"});
			Register(typeof(Matrix), new MatrixVectorSpace());
#endif
		}

		public delegate bool TypePredicate(object x);
		public delegate object Constructor3D(Single x, Single y, Single z);
		public delegate object Constructor4D(Single x, Single y, Single z, Single w);
		public delegate Single Accessor(object x);

#if WIRE_IN_DIRECT3D
		static object D3DVector3Constructor(Single x, Single y, Single z) {
			return new Vector3(x, y, z);
		}
		static object D3DVector4Constructor(Single x, Single y, Single z, Single w) {
			return new Vector4(x, y, z, w);
		}
		static bool IsD3DVector3(object o) {
			return o is Vector3;
		}
		static bool IsD3DVector4(object o) {
			return o is Vector4;
		}

		static Single GetV3X(object o) {
			return ((Vector3)o).X;
		}
		static Single GetV3Y(object o) {
			return ((Vector3)o).Y;
		}
		static Single GetV3Z(object o) {
			return ((Vector3)o).Z;
		}

		static Single GetV4X(object o) {
			return ((Vector4)o).X;
		}
		static Single GetV4Y(object o) {
			return ((Vector4)o).Y;
		}
		static Single GetV4Z(object o) {
			return ((Vector4)o).Z;
		}
		static Single GetV4W(object o) {
			return ((Vector4)o).W;
		}

		public static TypePredicate Is3Vector = new TypePredicate(IsD3DVector3);
		public static TypePredicate Is4Vector = new TypePredicate(IsD3DVector4);
		public static Constructor3D Make3Vector = new Constructor3D(D3DVector3Constructor);
		public static Accessor[] Vector3Accessors = new Accessor[] {new Accessor(GetV3X), new Accessor(GetV3Y), new Accessor(GetV3Z) };
		public static Constructor4D Make4Vector = new Constructor4D(D3DVector4Constructor);
		public static Accessor[] Vector4Accessors = new Accessor[] {new Accessor(GetV4X), new Accessor(GetV4Y), new Accessor(GetV4Z), new Accessor(GetV4Z) };
#else
		static object Default3DConstructor(Single x, Single y, Single z) {
			throw new Exception("Can't make 3D vectors without loading a 3D graphics package.");
		}
		static object Default4DConstructor(Single x, Single y, Single z, Single w) {
			throw new Exception("Can't make 4D vectors without loading a 3D graphics package.");
		}
		static bool DefaultExtendedVectorTypePredicate(object x) {
			return false;
		}
		public static TypePredicate Is3Vector = new TypePredicate(DefaultExtendedVectorTypePredicate);
		public static TypePredicate Is4Vector = new TypePredicate(DefaultExtendedVectorTypePredicate);
		public static Constructor3D Make3Vector = new Constructor3D(Default3DConstructor);
		public static Accessor[] Vector3Accessors;
		public static Constructor4D Make4Vector = new Constructor4D(Default4DConstructor);
		public static Accessor[] Vector4Accessors;
#endif

		
		public static void Register3VectorOps(TypePredicate is3Vector, Constructor3D make3Vector, Accessor[] accessors) {
			Is3Vector = is3Vector;
			Make3Vector = make3Vector;
			Vector3Accessors = accessors;
		}

		public static void Register4VectorOps(TypePredicate is4Vector, Constructor4D make4Vector, Accessor[] accessors) {
			Is4Vector = is4Vector;
			Make4Vector = make4Vector;
			Vector4Accessors = accessors;
		}

		private static System.Collections.Hashtable vectorSpaceTable = new System.Collections.Hashtable();
		public static void Register(Type type, VectorSpace v) {
			vectorSpaceTable[type] = v;
		}
		public static bool IsVectorSpace(Type t) {
			return vectorSpaceTable.ContainsKey(t);
		}

		public static bool IsVectorSpaceObject(object o) {
			return IsVectorSpace(o.GetType());
		}

		public static bool IsVectorType(Type t) {
#if WIRE_IN_DIRECT3D
			return t==typeof(System.Drawing.Point) || t==typeof(System.Drawing.PointF) || t==typeof(Vector3) || t==typeof(Vector4);
#else
			return t==typeof(System.Drawing.Point) || t==typeof(System.Drawing.PointF);
#endif
		}

		public static bool IsVector(object o) {
			return IsVectorType(o.GetType());
		}

		public static bool Is2DVectorType(Type t) {
			return t==typeof(System.Drawing.Point) || t==typeof(System.Drawing.PointF);
		}

		public static bool Is2DVector(object o) {
			return Is2DVectorType(o.GetType());
		}

		protected static void CantDo(string operation, object vector) {
			//throw new ArgumentException("Can't perform "+operation+" on "+vector.GetType().Name+" with selected arguments");
			throw new ArithmeticTypeException("value", vector);
		}
		protected static void CantDoNoArgs(string operation, object vector) {
			//throw new ArgumentException("Can't perform "+operation+" on "+vector.GetType().Name);
			throw new ArithmeticTypeException("value", vector);
		}

		public static VectorSpace GetSpace(object vector, string operation) {
			VectorSpace v = vectorSpaceTable[vector.GetType()] as VectorSpace;
			if (v != null)
				return v;
			else {
				CantDo(operation, vector);
				return null;
			}
		}

		//Generic comparison operator
		public static new bool Equals(object a, object b) {
			if (a==b)
				return true;
			VectorSpace v = vectorSpaceTable[a.GetType()] as VectorSpace;

			if (v==null)
				return false;   // a==b has already failed.
			else
				return GetSpace(a, "equality comparison").CheckEquality(a, b);
		}
		public virtual bool CheckEquality(object a, object b) {
			return false; // If we get this far, then the a==b test has already failed.
		}

		// Generic multiplication of non-numeric values
		public static object Multiply(object a, object b) {
			if (Utilities.IsNumber(a)) {
				if (Utilities.IsNumber(b))
					return Convert.ToDouble(a)*Convert.ToDouble(b);
				else
					return ScalarMultiply(Convert.ToDouble(a), b);
			} else if (Utilities.IsNumber(b))
				return ScalarMultiply(Convert.ToDouble(b), a);
			else
				return VectorMultiply(a, b);
		}

		// Scalar multiplication
		public static object ScalarMultiply(double scalar, object vector) {
			return GetSpace(vector,"scalar multiplication").ScalarMultiple(scalar, vector);
		}
		public virtual object ScalarMultiple(double scalar, object vector) {
			CantDo("scalar multiplication", vector);
			return null;
		}

		// Addition
		public static object Add(object vector1, object vector2) {
			return GetSpace(vector1, "addition").Sum(vector1, vector2);
		}
		public virtual object Sum(object vector1, object vector2) {
			CantDo("addition", vector1);
			return null;
		}

		// Unary Negation
		public static object Negate(object vector) {
			return GetSpace(vector, "negation").Negation(vector);
		}
		public virtual object Negation(object vector) {
			CantDoNoArgs("negation", vector);
			return null;
		}

		// Unit vectors
		public static object UnitVector(object vector) {
			return GetSpace(vector, "unit vector").Unit(vector);
		}
		public virtual object Unit(object vector) {
			CantDoNoArgs("unit vector", vector);
			return null;
		}

		// Subtraction
		public static object Subtract(object vector1, object vector2) {
			return GetSpace(vector1, "subtraction").Difference(vector1,  vector2);
		}
		public virtual object Difference(object vector1, object vector2) {
			CantDo("subtraction", vector1);
			return null;
		}

		// Vector product
		public static object VectorMultiply(object vector1, object vector2) {
			return GetSpace(vector1, "multiplication").VectorProduct(vector1, vector2);
		}
		public virtual object VectorProduct(object vector1, object vector2) {
			CantDo("multiplication", vector1);
			return null;
		}

		// Inner product
		public static double InnerMultiply(object vector1, object vector2) {
			return GetSpace(vector1, "inner product").InnerProduct(vector1, vector2);
		}
		public virtual double InnerProduct(object vector1, object vector2) {
			CantDo("inner product", vector1);
			return 0;
		}

		// Norms
		public static double Magnitude(object vector) {
			return GetSpace(vector, "Magnitude").Norm(vector);
		}
		public virtual double Norm(object vector) {
			CantDoNoArgs("Magnitude", vector);
			return 0;
		}

	}

	class ColorVectorSpace : VectorSpace {
		public static Byte Clip(int x) {
			return (byte)Math.Max(0, Math.Min(255, x));
		}

		public Color ToColor(object o, string operation) {
			if (o is Color)
				return (Color)o;
			else {
				CantDo(operation, o);
				return Color.Black;
			}
		}

		public override bool CheckEquality(object a, object b) {
			if (!(b is Color))
				return false;
			Color ca = (Color)a;
			Color cb = (Color)b;
			return ca.R==cb.R && ca.G==cb.G && ca.B==cb.B;
		}

		public override object Sum(object a, object b) {
			Color c1 = (Color)a;
			Color c2 = ToColor(b, "addition");

			return Color.FromArgb(Clip(Math.Max(c1.A,c2.A)), Clip(c1.R+c2.R), Clip(c1.G+c2.G), Clip(c1.B+c2.B));
		}
		public override object Difference(object a, object b) {
			Color c1 = (Color)a;
			Color c2 = ToColor(b, "subtraction");

			return Color.FromArgb(Clip(Math.Max(c1.A,c2.A)), Clip(c1.R-c2.R), Clip(c1.G-c2.G), Clip(c1.B-c2.B));
		}
		public override object VectorProduct(object a, object b) {
			Color c1 = (Color)a;
			Color c2 = ToColor(b, "multiplication");

			return Color.FromArgb(Clip(Math.Max(c1.A,c2.A)), (c1.R*c2.R)>>8, (c1.G*c2.G)>>8, (c1.B*c2.B)>>8);
		}
		public override object ScalarMultiple(double a, object b) {
			Color c2 = (Color)b;

			return Color.FromArgb(c2.A, Clip((int)Math.Round(a*c2.R)), Clip((int)Math.Round(a*c2.G)), Clip((int)Math.Round(a*c2.B)));
		}

		public override double Norm(object vector) {
			Color c = (Color)vector;
			return c.R+c.G+c.B;
		}
	}

	class PointVectorSpace : VectorSpace {
		public override bool CheckEquality(object a, object b) {
			Point pa = (Point)a;
			if (b is Point) {
				Point pb = (Point)b;
				return pa.X==pb.X && pa.Y==pb.Y;
			} else if (b is PointF) {
				PointF pfb = (PointF)b;
				return pa.X==pfb.X && pa.Y==pfb.Y;
			} else
				return false;
		}

		public override object Sum(object a, object b) {
			Point pa = (Point)a;

			if (b is Point) {
				Point pb = (Point)b;
				return new Point(pa.X+pb.X, pa.Y+pb.Y);
			} else {
				if (b is PointF) {
					PointF fb = (PointF)b;

					return new PointF(pa.X+fb.X, pa.Y+fb.Y);
				} else {
					CantDo("addition",b);
					return null;
				}
			}
		}

		public override object Difference(object a, object b) {
			Point pa = (Point)a;

			if (b is Point) {
				Point pb = (Point)b;
				return new Point(pa.X-pb.X, pa.Y-pb.Y);
			} else {
				if (b is PointF) {
					PointF fb = (PointF)b;

					return new PointF(pa.X-fb.X, pa.Y-fb.Y);
				} else {
					CantDo("subtraction",b);
					return null;
				}
			}
		}

		public override object Negation(object p) {
			Point pt = (Point)p;

			return new Point(-pt.X, -pt.Y);
		}

		public override object ScalarMultiple(double s, object pt) {
			Point p = (Point)pt;
			int si=(int)s;

			if (s==si)
				return new Point(si*p.X, si*p.Y);
			else
				return new PointF((float)s*p.X, (float)s*p.Y);
		}

		public override double InnerProduct(object o1, object o2) {
			Point p1 = (Point)o1;

			if (o2 is Point) {
				Point p2 = (Point)o2;
				return (p1.X*p2.X)+(p1.Y*p2.Y);
			} else if (o2 is PointF) {
				PointF f2 = (PointF)o2;
				return (p1.X*f2.X)+(p1.Y*f2.Y);
			} else {
				CantDo("inner product", o2);
				return 0;
			}
		}

		public override double Norm(object o) {
			Point p = (Point)o;

			return Math.Sqrt(p.X*p.X+p.Y*p.Y);
		}

		public override object Unit(object o) {
			Point p = (Point)o;
			double scale = 1.0/Math.Sqrt(p.X*p.X+p.Y*p.Y);

			return new PointF((float)(scale*p.X), (float)(scale*p.Y));
		}
	}

	public class PointFVectorSpace : VectorSpace {
		public static PointF ToPointF(object o, string operation) {
			if (o is PointF)
				return (PointF)o;
			else if (o is Point) {
				Point p = (Point)o;
				return new PointF(p.X, p.Y);
			} else {
				CantDo(operation, o);
				return new PointF(0,0);
			}
		}

		public override bool CheckEquality(object a, object b) {
			PointF pfa = (PointF)a;

			if (b is Point) {
				Point pb = (Point)b;
				return pfa.X==pb.X && pfa.Y==pb.Y;
			} else if (b is PointF) {
				PointF pfb = (PointF)b;
				return pfa.X==pfb.X && pfa.Y==pfb.Y;
			} else
				return false;
		}

		public override object Sum(object a, object b) {
			PointF pa = (PointF)a;
			PointF pb = ToPointF(b, "addition");

			return new PointF(pa.X+pb.X, pa.Y+pb.Y);
		}

		public override object Difference(object a, object b) {
			PointF pa = (PointF)a;
			PointF pb = ToPointF(b, "subtraction");

			return new PointF(pa.X-pb.X, pa.Y-pb.Y);
		}

		public override object Negation(object p) {
			PointF pt = (PointF)p;

			return new PointF(-pt.X, -pt.Y);
		}

		public override object ScalarMultiple(double s, object pt) {
			PointF p = (PointF)pt;

			return new PointF((float)s*p.X, (float)s*p.Y);
		}

		public override double InnerProduct(object o1, object o2) {
			PointF p1 = (PointF)o1;
			PointF p2 = ToPointF(o2, "inner product");

			return (p1.X*p2.X)+(p1.Y*p2.Y);
		}

		public override double Norm(object o) {
			PointF p = ToPointF(o, "magnitude");

			return Math.Sqrt(p.X*p.X+p.Y*p.Y);
		}

		public override object Unit(object o) {
			PointF p = (PointF)o;
			double scale = 1.0/Math.Sqrt(p.X*p.X+p.Y*p.Y);

			return new PointF((float)(scale*p.X), (float)(scale*p.Y));
		}

		public static PointF UnitVectorPerpendicular(object o) {
			PointF p = (PointF)o;
			double scale = 1.0/Math.Sqrt(p.X*p.X+p.Y*p.Y);

			return new PointF((float)(scale*p.Y), (float)(-scale*p.X));
		}

		public static PointF RotateRadians(object o, double angle) {
			PointF p = ToPointF(o, "rotation");
			double cos = Math.Cos(angle);
			double sin = Math.Sin(angle);

			return new PointF((float)(cos*p.X-sin*p.Y), (float)(sin*p.X+cos*p.Y));
		}

		public static PointF RotateDegrees(object o, double angle) {
			return RotateRadians(o, 2*Math.PI*angle/360.0);
		}
	}

#if WIRE_IN_DIRECT3D
	class Vector3VectorSpace : VectorSpace {
		public override bool CheckEquality(object a, object b) {
			if (a is Vector3 && b is Vector3)
				return ((Vector3)a)==((Vector3)b);
			else return false;
		}

		public override object Sum(object a, object b) {
			Vector3 pa = (Vector3)a;

			if (b is Vector3) {
				Vector3 pb = (Vector3)b;
				return pa+pb;
			} else {
				CantDo("addition",b);
				return null;
			}
		}

		public override object Difference(object a, object b) {
			Vector3 pa = (Vector3)a;

			if (b is Vector3) {
				Vector3 pb = (Vector3)b;
				return pa-pb;
			} else {
				CantDo("subtraction",b);
				return null;
			}
		}

		public override object Negation(object p) {
			return -((Vector3)p);
		}

		public override object ScalarMultiple(double s, object pt) {
			return ((Vector3)pt)*((float)s);
		}

		public override double InnerProduct(object o1, object o2) {
			return Vector3.Dot((Vector3)o1, (Vector3)o2);
		}

		public override double Norm(object o) {
			return ((Vector3)o).Length();
		}

		public override object Unit(object o) {
			Vector3 p = (Vector3)o;
			return p*(1/p.Length());
		}
	}

		class Vector4VectorSpace : VectorSpace {
		public override bool CheckEquality(object a, object b) {
			if (a is Vector4 && b is Vector4)
				return ((Vector4)a)==((Vector4)b);
			else return false;
		}

		public override object Sum(object a, object b) {
			Vector4 pa = (Vector4)a;

			if (b is Vector4) {
				Vector4 pb = (Vector4)b;
				return pa+pb;
			} else {
				CantDo("addition",b);
				return null;
			}
		}

		public override object Difference(object a, object b) {
			Vector4 pa = (Vector4)a;

			if (b is Vector4) {
				Vector4 pb = (Vector4)b;
				return pa-pb;
			} else {
				CantDo("subtraction",b);
				return null;
			}
		}

		public override object Negation(object p) {
			return -((Vector4)p);
		}

		public override object ScalarMultiple(double s, object pt) {
			return ((Vector4)pt)*((float)s);
		}

		public override double InnerProduct(object o1, object o2) {
			return Vector4.Dot((Vector4)o1, (Vector4)o2);
		}

		public override double Norm(object o) {
			return ((Vector4)o).Length();
		}

		public override object Unit(object o) {
			Vector4 p = (Vector4)o;
			return p*(1/p.Length());
		}
	}

	class MatrixVectorSpace : VectorSpace {
		public override bool CheckEquality(object a, object b) {
			if (a is Matrix && b is Matrix)
				return ((Matrix)a)==((Matrix)b);
			else return false;
		}

		public override object Sum(object a, object b) {
			Matrix pa = (Matrix)a;

			if (b is Matrix) {
				Matrix pb = (Matrix)b;
				return pa+pb;
			} else {
				CantDo("addition",b);
				return null;
			}
		}

		public override object Difference(object a, object b) {
			Matrix pa = (Matrix)a;

			if (b is Matrix) {
				Matrix pb = (Matrix)b;
				return pa-pb;
			} else {
				CantDo("subtraction",b);
				return null;
			}
		}

//		public override object Negation(object p) {
//			return -((Matrix)p);
//		}

//		public override object ScalarMultiple(double s, object pt) {
//			return ((Matrix)pt)*((float)s);
//		}

//		public override double InnerProduct(object o1, object o2) {
//			return Matrix.Dot((Matrix)o1, (Matrix)o2);
//		}

//		public override double Norm(object o) {
//			return ((Matrix)o).Length();
//		}

//		public override object Unit(object o) {
//			Matrix p = (Matrix)o;
//			return p*(1/p.Length());
//		}
	}
#endif

    //
	// This *probably* ought to get converted to use typcodes, but I'm not actually sure it would be any
	// faster since I don't know how fast the Object.GetType operation is and there's not Object.GetTypeCode
	// operation.  Also, it's probably only faster for the n-argument case.
	//

	[Serializable]
	class PrimitiveProcedureAdd : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureAdd(Object name) {
			this.name=name;
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("number"), Symbol.Intern("...") };
			}
		}
		string Documentation {
			get {
				return "Returns the sum of all arguments";
			}
		}
		public override object Call(object arg) {
			if (Utilities.IsNumber(arg))
				return arg;
			else {
				VectorSpace.GetSpace(arg, "addition");  // will throw error if arg isn't a vector space.
				return arg;
			}
		}
		public override object Call(object arg1, object arg2) {
			checked {
				if (Utilities.IsNumber(arg1, arg2) ){
					if (arg1 is int && arg2 is int)
						return (int)arg1+(int)arg2;
					else
						return System.Convert.ToDouble(arg1) + System.Convert.ToDouble(arg2);
				} else
					return VectorSpace.Add(arg1, arg2);
			}
		}
		public override object Call(object arg1, object arg2, object arg3) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3)) {
					if (arg1 is int && arg2 is int && arg3 is int)
						return (int)arg1+(int)arg2+(int)arg3;
					else
						return System.Convert.ToDouble(arg1) + System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3);
				} else
					return VectorSpace.Add(VectorSpace.Add(arg1, arg2), arg3);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int)
						return (int)arg1+(int)arg2+(int)arg3+(int)arg4;
					else
						return System.Convert.ToDouble(arg1) + System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3)+System.Convert.ToDouble(arg4);
				} else
					return VectorSpace.Add(VectorSpace.Add(VectorSpace.Add(arg1, arg2), arg3), arg4);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4, arg5)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int && arg5 is int)
                        return (int)arg1 + (int)arg2 + (int)arg3 + (int)arg4 + (int)arg5;
					else
						return System.Convert.ToDouble(arg1) + System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3)+System.Convert.ToDouble(arg4)+System.Convert.ToDouble(arg5);
				} else
					return VectorSpace.Add(VectorSpace.Add(VectorSpace.Add(VectorSpace.Add(arg1, arg2), arg3), arg4), arg5);
			}
		}

		public override object Apply(object[] args) {
			if (args.Length==0)
				return 0;
			if (Utilities.IsNumber(args[0])) {
				int result=0;
				int i=0;

				for (;i<args.Length;i++) {
					object x=args[i];

					checked {
						if (x is int)
							result += (int)x;
						else
							goto dofloats;
					}
				}
				return result;

			dofloats:
				double fresult = result;
				for (;i<args.Length; i++)
					checked {
						fresult += System.Convert.ToDouble(args[i]);
					}

				return fresult;
			} else {
				object gresult = args[0];
				for (int j = 1;j<args.Length; j++)
					checked {
						gresult = VectorSpace.Add(gresult, args[j]);
					}
				return gresult;
			}
		}
	}

	[Serializable]
	class PrimitiveProcedureMultiply : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureMultiply(Object name) {
			this.name=name;
		}
		string Documentation {
			get {
				return "Returns the product of all arguments";
			}
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("number"), Symbol.Intern("...") };
			}
		}
		public override object Apply(object[] args) {
			int result=1;
			int i=0;

			for (;i<args.Length;i++) {
				object x=args[i];

				if (x is int)
					checked {
						result *= (int)x;
					}
				else
					goto dogeneric;
			}
			return result;

			dogeneric:
				object gresult = args[0];
			for (i=1;i<args.Length; i++)
				gresult = VectorSpace.Multiply(gresult, args[i]);

			return gresult;
		}
		public override object Call(object arg) {
			if (arg is int || arg is double || arg is float)
				return 1;
			else {
				VectorSpace.GetSpace(arg, "multiplication");  // will throw an error if this isn't a vector
				return arg;
			}
		}
		public override object Call(object arg1, object arg2) {
			checked {
				if (Utilities.IsNumber(arg1, arg2)) {
					if (arg1 is int && arg2 is int)
						return (int)arg1*(int)arg2;
					else
						return System.Convert.ToDouble(arg1) * System.Convert.ToDouble(arg2);
				} else
					return VectorSpace.Multiply(arg1, arg2);
			}
		}
		public override object Call(object arg1, object arg2, object arg3) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3)) {
					if (arg1 is int && arg2 is int && arg3 is int)
						return (int)arg1*(int)arg2*(int)arg3;
					else
						return System.Convert.ToDouble(arg1) * System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3);
				} else
					return VectorSpace.Multiply(VectorSpace.Multiply(arg1, arg2), arg3);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int)
						return (int)arg1*(int)arg2*(int)arg3*(int)arg4;
					else
						return System.Convert.ToDouble(arg1) * System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3)*System.Convert.ToDouble(arg4);
				} else
					return VectorSpace.Multiply(VectorSpace.Multiply(VectorSpace.Multiply(arg1, arg2), arg3), arg4);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4, arg5)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int && arg5 is int)
                        return (int)arg1 * (int)arg2 * (int)arg3 * (int)arg4 * (int)arg5;
					else
						return System.Convert.ToDouble(arg1) * System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3)*System.Convert.ToDouble(arg4)*System.Convert.ToDouble(arg5);
				} else
					return VectorSpace.Multiply(VectorSpace.Multiply(VectorSpace.Multiply(VectorSpace.Multiply(arg1, arg2), arg3), arg4), arg5);
			}
		}
	}

	[Serializable]
	class PrimitiveProcedureSubtract : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureSubtract(Object name) {
			this.name=name;
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("number"), Symbol.Intern("...") };
			}
		}
		string Documentation {
			get {
				return "Returns the first argument minus the sum of the rest of the arguments";
			}
		}
		public override object Apply(object[] args) {
			if (args.Length==0)
				throw new ArgumentCountException(args, new object[] { Symbol.Intern("number"), Symbol.Intern("number") });

			if (Utilities.IsNumber(args[0])) {
				int len=args.Length;
				int result=0;
				double fresult=0;
				int i=1;

				if (len==0)
					return 0;
				object arg0 = args[0];
				if (len==1) {
					if (arg0 is int)
							return -(int)arg0;
					else
						return -System.Convert.ToDouble(arg0);
				} else if (arg0 is int)
					result = (int)arg0;
				else {
					fresult = System.Convert.ToDouble(arg0);
					goto dofloat;
				}
				for (;i<args.Length;i++) {
					object x=args[i];

					if (x is int)
						result -= (int)x;
					else
						goto dofloats;
				}
				return result;

			dofloats:
				fresult = result;
			dofloat:
				for (;i<args.Length; i++)
					checked {
						fresult -= System.Convert.ToDouble(args[i]);
					}

				return fresult;
			} else {
				object gresult = args[0];
				for (int j = 1;j<args.Length; j++)
					gresult = VectorSpace.Subtract(gresult, args[j]);
				return gresult;
			}
		}

		public override object Call(object arg) {
			if (arg is int)
				return -(int)arg;
			else if (arg is double)
				return -(double)arg;
			else if (arg is float)
				return -(float)arg;
			else
				return VectorSpace.Negate(arg);
		}
		public override object Call(object arg1, object arg2) {
			checked {
				if (Utilities.IsNumber(arg1, arg2)) {
					if (arg1 is int && arg2 is int)
						return (int)arg1-(int)arg2;
					else
						return System.Convert.ToDouble(arg1) - System.Convert.ToDouble(arg2);
				} else
					return VectorSpace.Subtract(arg1, arg2);
			}
		}
		public override object Call(object arg1, object arg2, object arg3) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3)) {
					if (arg1 is int && arg2 is int && arg3 is int)
						return (int)arg1-((int)arg2+(int)arg3);
					else
						return System.Convert.ToDouble(arg1) - (System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3));
				} else
					return VectorSpace.Subtract(VectorSpace.Subtract(arg1, arg2), arg3);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int)
						return (int)arg1-((int)arg2+(int)arg3+(int)arg4);
					else
						return System.Convert.ToDouble(arg1) - (System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3)+System.Convert.ToDouble(arg4));
				} else
					return VectorSpace.Subtract(VectorSpace.Subtract(VectorSpace.Subtract(arg1, arg2), arg3), arg4);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			checked {
				if (Utilities.IsNumber(arg1, arg2, arg3, arg4, arg5)) {
					if (arg1 is int && arg2 is int && arg3 is int && arg4 is int && arg5 is int)
                        return (int)arg1 - ((int)arg2 + (int)arg3 + (int)arg4 + (int)arg5);
					else
						return System.Convert.ToDouble(arg1) - (System.Convert.ToDouble(arg2)+System.Convert.ToDouble(arg3)+System.Convert.ToDouble(arg4)+System.Convert.ToDouble(arg5));
				} else
					return VectorSpace.Subtract(VectorSpace.Subtract(VectorSpace.Subtract(VectorSpace.Subtract(arg1, arg2), arg3), arg4), arg5);
			}
		}

	}

	[Serializable]
	class PrimitiveProcedureQuotient : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureQuotient(Object name) {
			this.name=name;
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("numerator"), Symbol.Intern("denominator") };
			}
		}
		string Documentation {
			get {
				return "Returns the result of dividing numerator by denomenator, rounding toward zero; result is always an integer";
			}
		}
		public override object Apply(object[] args) {
			int len=args.Length;
			int divisor=1;

			if (len==0)
				throw new ArgumentCountException(args, new object[] { Symbol.Intern("numerator"), Symbol.Intern("denominator") });

			for (int i=1;i<args.Length;i++)
				checked {
					if (!Utilities.IsInteger(args[i]))
						throw new ArgumentTypeException("divisor", args[i], PseudoType.Integer);
					divisor *= Convert.ToInt32(args[i]);
				}
			if (!Utilities.IsInteger(args[0]))
				throw new ArgumentTypeException("numerator", args[0], PseudoType.Integer);
			return checked(Convert.ToInt32(args[0])/divisor);
		}
		public override object Call(object arg) {
			if (!Utilities.IsInteger(arg))
				throw new ArgumentTypeException("divisor", arg, PseudoType.Integer);
			return checked(1/Convert.ToInt32(arg)); 
		}
		public override object Call(object arg1, object arg2) {
			if (!Utilities.IsInteger(arg1))
				throw new ArgumentTypeException("numerator", arg1, PseudoType.Integer);
			if (!Utilities.IsInteger(arg2))
				throw new ArgumentTypeException("divisor", arg2, PseudoType.Integer);
			return checked(Convert.ToInt32(arg1)/Convert.ToInt32(arg2));
		}
		public override object Call(object arg1, object arg2, object arg3) {
			if (!Utilities.IsInteger(arg1))
				throw new ArgumentTypeException("numerator", arg1, PseudoType.Integer);
			if (!Utilities.IsInteger(arg2))
				throw new ArgumentTypeException("divisor", arg2, PseudoType.Integer);
			if (!Utilities.IsInteger(arg3))
				throw new ArgumentTypeException("divisor", arg3, PseudoType.Integer);
			return checked(Convert.ToInt32(arg1)/(Convert.ToInt32(arg2)*Convert.ToInt32(arg3)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			if (!Utilities.IsInteger(arg1))
				throw new ArgumentTypeException("numerator", arg1, PseudoType.Integer);
			if (!Utilities.IsInteger(arg2))
				throw new ArgumentTypeException("divisor", arg2, PseudoType.Integer);
			if (!Utilities.IsInteger(arg3))
				throw new ArgumentTypeException("divisor", arg3, PseudoType.Integer);
			if (!Utilities.IsInteger(arg4))
				throw new ArgumentTypeException("divisor", arg4, PseudoType.Integer);
			return checked(Convert.ToInt32(arg1)/(Convert.ToInt32(arg2)*Convert.ToInt32(arg3)*Convert.ToInt32(arg4)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			if (!Utilities.IsInteger(arg1))
				throw new ArgumentTypeException("numerator", arg1, PseudoType.Integer);
			if (!Utilities.IsInteger(arg2))
				throw new ArgumentTypeException("divisor", arg2, PseudoType.Integer);
			if (!Utilities.IsInteger(arg3))
				throw new ArgumentTypeException("divisor", arg3, PseudoType.Integer);
			if (!Utilities.IsInteger(arg4))
				throw new ArgumentTypeException("divisor", arg4, PseudoType.Integer);
			if (!Utilities.IsInteger(arg5))
				throw new ArgumentTypeException("divisor", arg5, PseudoType.Integer);
			return checked(Convert.ToInt32(arg1)/(Convert.ToInt32(arg2)*Convert.ToInt32(arg3)*Convert.ToInt32(arg4)));
		}
	}

	[Serializable]
	class PrimitiveProcedureDivide : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureDivide(Object name) {
			this.name=name;
		}
		string Documentation {
			get {
				return "Returns numerator divided by denomenator; result is always a floating-point number";
			}
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("numerator"), Symbol.Intern("denomenator") };
			}
		}
		public override object Apply(object[] args) {
			int len=args.Length;
			double divisor=1.0;

			if (len==0)
				throw new ArgumentCountException(args, new object[] { Symbol.Intern("numerator"), Symbol.Intern("denominator") });

			if (args.Length==1) {
				object arg = args[0];
				if (Utilities.IsNumber(arg))
					return checked(1.0/System.Convert.ToDouble(arg));
				else
					throw new ArgumentTypeException("numerator", arg, PseudoType.Number);
			} else {	
				for (int i=1;i<args.Length;i++)
					checked {
						if (!Utilities.IsInteger(args[i]))
							throw new ArgumentTypeException("divisor", args[i], PseudoType.Number);
						divisor *= System.Convert.ToDouble(args[i]);
					}

				if (Utilities.IsNumber(args[0]))
					return checked(Convert.ToDouble(args[0])/divisor);
				else
					return VectorSpace.ScalarMultiply(1.0/divisor, args[0]);
			}
		}
		public override object Call(object arg) {
			if (Utilities.IsNumber(arg))
				return checked(1.0/System.Convert.ToDouble(arg));
			else
				throw new ArgumentException("Argument to division must be a number");
		}
		public override object Call(object arg1, object arg2) {
			checked {
				if (!Utilities.IsNumber(arg2))
					throw new ArgumentTypeException("divisor", arg2, PseudoType.Number);
				if (Utilities.IsNumber(arg1))
					return System.Convert.ToDouble(arg1) / System.Convert.ToDouble(arg2);
				else
					return VectorSpace.ScalarMultiply(1.0/Convert.ToDouble(arg2), arg1);
			}
		}
		public override object Call(object arg1, object arg2, object arg3) {
			checked {
				if (!Utilities.IsNumber(arg2))
					throw new ArgumentTypeException("divisor", arg2, PseudoType.Number);
				if (!Utilities.IsNumber(arg3))
					throw new ArgumentTypeException("divisor", arg3, PseudoType.Number);
				if (Utilities.IsNumber(arg1))	
					return System.Convert.ToDouble(arg1) / (System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3));
				else
					return VectorSpace.ScalarMultiply(1.0/(Convert.ToDouble(arg2)*Convert.ToDouble(arg3)), arg1);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			checked {
				if (!Utilities.IsNumber(arg2))
					throw new ArgumentTypeException("divisor", arg2, PseudoType.Number);
				if (!Utilities.IsNumber(arg3))
					throw new ArgumentTypeException("divisor", arg3, PseudoType.Number);
				if (!Utilities.IsNumber(arg4))
					throw new ArgumentTypeException("divisor", arg4, PseudoType.Number);
				if (Utilities.IsNumber(arg1))	
					return System.Convert.ToDouble(arg1) / (System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3)*System.Convert.ToDouble(arg4));
				else
					return VectorSpace.ScalarMultiply(1.0/(Convert.ToDouble(arg2)*Convert.ToDouble(arg3)*Convert.ToDouble(arg4)), arg1);
			}
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			checked {
				if (!Utilities.IsNumber(arg2))
					throw new ArgumentTypeException("divisor", arg2, PseudoType.Number);
				if (!Utilities.IsNumber(arg3))
					throw new ArgumentTypeException("divisor", arg3, PseudoType.Number);
				if (!Utilities.IsNumber(arg4))
					throw new ArgumentTypeException("divisor", arg4, PseudoType.Number);
				if (!Utilities.IsNumber(arg5))
					throw new ArgumentTypeException("divisor", arg5, PseudoType.Number);
				if (Utilities.IsNumber(arg1))	
					return System.Convert.ToDouble(arg1) / (System.Convert.ToDouble(arg2)*System.Convert.ToDouble(arg3)*System.Convert.ToDouble(arg4)*System.Convert.ToDouble(arg5));
				else
					return VectorSpace.ScalarMultiply(1.0/(Convert.ToDouble(arg2)*Convert.ToDouble(arg3)*Convert.ToDouble(arg4)*Convert.ToDouble(arg5)), arg1);
			}
		}
	}

	[Serializable]
	class PrimitiveProcedureMin : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureMin(Object name) {
			this.name=name;
		}
		string Documentation {
			get {
				return "Returns smallest of the numbers";
			}
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("number"), Symbol.Intern("...") };
			}
		}

		public override object Call() {
			throw new ArgumentCountException(new object[0], Arglist);
		}

		public override object Call(object arg1) {
			if (!Utilities.IsNumber(arg1))
				throw new ArgumentTypeException("number", arg1, PseudoType.Number);
			return arg1;
		}

		public override object Call(object arg1, object arg2) {
			if (Utilities.NumberIsInteger(arg1, arg2))
				return Math.Min(Convert.ToInt32(arg1), Convert.ToInt32(arg2));
			else
				return Math.Min(Convert.ToDouble(arg1), Convert.ToDouble(arg2));
		}
		public override object Call(object arg1, object arg2, object arg3) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3))
				return Math.Min(Convert.ToInt32(arg1), Math.Min(Convert.ToInt32(arg2), Convert.ToInt32(arg3)));
			else
				return Math.Min(Convert.ToDouble(arg1), Math.Min(Convert.ToDouble(arg2), Convert.ToDouble(arg3)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4))
				return Math.Min(Math.Min(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Min(Convert.ToInt32(arg3), Convert.ToInt32(arg4)));
			else
				return Math.Min(Math.Min(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Min(Convert.ToDouble(arg3), Convert.ToDouble(arg4)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4 ,arg5))
				return Math.Min(Math.Min(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Min(Math.Min(Convert.ToInt32(arg3), Convert.ToInt32(arg4)), Convert.ToInt32(arg5)));
			else
				return Math.Min(Math.Min(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Min(Math.Min(Convert.ToDouble(arg3), Convert.ToDouble(arg4)), Convert.ToDouble(arg5)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4, arg5, arg6))
				return Math.Min(Math.Min(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Min(Math.Min(Convert.ToInt32(arg3), Convert.ToInt32(arg4)), Math.Min(Convert.ToInt32(arg5), Convert.ToInt32(arg6))));
			else
				return Math.Min(Math.Min(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Min(Math.Min(Convert.ToDouble(arg3), Convert.ToDouble(arg4)), Math.Min(Convert.ToDouble(arg5), Convert.ToDouble(arg6))));
		}
		public override object Apply(object[] args) {
			int iresult;
			double fresult;
			int i=1;

			if (Utilities.NumberIsInteger(args[0])) {
				iresult = Convert.ToInt32(args[0]);
				for (; i<args.Length;i++) {
					if (Utilities.NumberIsInteger(args[i]))
						iresult = Math.Min(iresult, Convert.ToInt32(args[i]));
					else {
						fresult = (double)iresult;
						goto fcase;
					}
				}
				return iresult;
			}

			fresult = Convert.ToDouble(args[0]);

			fcase:
				for (; i<args.Length; i++) {
					if (Utilities.IsNumber(args[i]))
						fresult = Math.Min(fresult, Convert.ToDouble(args[i]));
					else
						throw new ArgumentTypeException("number", args[i], PseudoType.Number);
				}
			return fresult;
		}
	}


	[Serializable]
	class PrimitiveProcedureMax : ApplyInterfaceProcedure, IArglist, IDocumentable {
		public PrimitiveProcedureMax(Object name) {
			this.name=name;
		}
		string Documentation {
			get {
				return "Returns smallest of the numbers";
			}
		}
		object[] Arglist {
			get {
				return new object[2] { Symbol.Intern("number"), Symbol.Intern("...") };
			}
		}

		public override object Call() {
			throw new ArgumentCountException(new object[0], Arglist);
		}

		public override object Call(object arg1) {
			if (!Utilities.IsNumber(arg1))
				throw new ArgumentTypeException("number", arg1, PseudoType.Number);
			return arg1;
		}

		public override object Call(object arg1, object arg2) {
			if (Utilities.NumberIsInteger(arg1, arg2))
				return Math.Max(Convert.ToInt32(arg1), Convert.ToInt32(arg2));
			else
				return Math.Max(Convert.ToDouble(arg1), Convert.ToDouble(arg2));
		}
		public override object Call(object arg1, object arg2, object arg3) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3))
				return Math.Max(Convert.ToInt32(arg1), Math.Max(Convert.ToInt32(arg2), Convert.ToInt32(arg3)));
			else
				return Math.Max(Convert.ToDouble(arg1), Math.Max(Convert.ToDouble(arg2), Convert.ToDouble(arg3)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4))
				return Math.Max(Math.Max(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Max(Convert.ToInt32(arg3), Convert.ToInt32(arg4)));
			else
				return Math.Max(Math.Max(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Max(Convert.ToDouble(arg3), Convert.ToDouble(arg4)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4, arg5))
				return Math.Max(Math.Max(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Max(Math.Max(Convert.ToInt32(arg3), Convert.ToInt32(arg4)), Convert.ToInt32(arg5)));
			else
				return Math.Max(Math.Max(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Max(Math.Max(Convert.ToDouble(arg3), Convert.ToDouble(arg4)), Convert.ToDouble(arg5)));
		}
		public override object Call(object arg1, object arg2, object arg3, object arg4, object arg5, object arg6) {
			if (Utilities.NumberIsInteger(arg1, arg2, arg3, arg4, arg5, arg6))
				return Math.Max(Math.Max(Convert.ToInt32(arg1), Convert.ToInt32(arg2)), Math.Max(Math.Max(Convert.ToInt32(arg3), Convert.ToInt32(arg4)), Math.Max(Convert.ToInt32(arg5), Convert.ToInt32(arg6))));
			else
				return Math.Max(Math.Max(Convert.ToDouble(arg1), Convert.ToDouble(arg2)), Math.Max(Math.Max(Convert.ToDouble(arg3), Convert.ToDouble(arg4)), Math.Max(Convert.ToDouble(arg5), Convert.ToDouble(arg6))));
		}
		public override object Apply(object[] args) {
			int iresult;
			double fresult;
			int i=1;

			if (Utilities.NumberIsInteger(args[0])) {
				iresult = Convert.ToInt32(args[0]);
				for (; i<args.Length;i++) {
					if (Utilities.NumberIsInteger(args[i]))
						iresult = Math.Max(iresult, Convert.ToInt32(args[i]));
					else {
						fresult = (double)iresult;
						goto fcase;
					}
				}
				return iresult;
			}

			fresult = Convert.ToDouble(args[0]);

			fcase:
				for (; i<args.Length; i++) {
					if (Utilities.IsNumber(args[i]))
						fresult = Math.Max(fresult, Convert.ToDouble(args[i]));
					else
						throw new ArgumentTypeException("number", args[i], PseudoType.Number);
				}
			return fresult;
		}
	}

}
